package cn.liuyang.tinyioc.beans.xml;

import cn.liuyang.tinyioc.beans.AbstractBeanDefinitionReader;
import cn.liuyang.tinyioc.beans.BeanDefinition;
import cn.liuyang.tinyioc.BeanReference;
import cn.liuyang.tinyioc.beans.PropertyValue;
import cn.liuyang.tinyioc.beans.io.ResourceLoader;
import cn.liuyang.tinyioc.beans.io.UrlResource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(String location) throws Exception {
        UrlResource urlResource = (UrlResource) getResourceLoader().getResource(location);
        InputStream inputStream = urlResource.getInputStream();
        doLoadBeanDefinitions(inputStream);
    }

    protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse(inputStream);

        registerBeanDefinitions(document);

        inputStream.close();
    }

    private void registerBeanDefinitions(Document document) {
        Element root = document.getDocumentElement();

        parseBeanDefinitions(root);
    }

    protected void parseBeanDefinitions(Element root) {

        NodeList nodes = root.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            if (node instanceof Element) {
                Element element = (Element) node;
                processBeanDefinitions(element);
            }
        }
    }

    private void processBeanDefinitions(Element element) {
        if ("bean".equals(element.getTagName())) {
            String beanName = element.getAttribute("name");
            String beanClassName = element.getAttribute("class");
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanClassName(beanClassName);

            processProperty(element, beanDefinition);

            getRegistry().put(beanName, beanDefinition);
        }
    }

    private void processProperty(Element element, BeanDefinition beanDefinition) {
        NodeList propertyNodeList = element.getElementsByTagName("property");
        for (int i = 0; i < propertyNodeList.getLength(); i++) {
            Node node = propertyNodeList.item(i);
            if (node instanceof Element) {
                Element propertyElement = (Element) node;
                String propertyName = propertyElement.getAttribute("name");
                String propertyValue = propertyElement.getAttribute("value");
                if (null == propertyValue || "".equals(propertyValue)) {
                    String ref = propertyElement.getAttribute("ref");
                    if (null == ref || "".equals(ref)) {
                        throw new IllegalArgumentException("Configuration problem: <property> element for " +
                                propertyValue + " must specify a ref or value");
                    }
                    BeanReference reference = new BeanReference(ref);
                    beanDefinition.getPropertyValues().addPropertyValues
                            (new PropertyValue(propertyName, reference));
                } else {
                    beanDefinition.getPropertyValues().addPropertyValues
                            (new PropertyValue(propertyName, propertyValue));
                }
            }
        }
    }


}
